package shard

import (
	"fmt"
	"hash"
	"hash/adler32"
	"hash/crc32"
	"hash/fnv"
	"sync"
)

type safemap struct {
	mtx sync.RWMutex
	mm  map[uint]any
}

func newSafemap() safemap {
	return safemap{mm: make(map[uint]any)}
}

func (s *safemap) Set(key uint, val any) {
	s.mtx.Lock()
	s.mm[key] = val
	s.mtx.Unlock()
}

func (s *safemap) Get(key uint) (any, bool) {
	s.mtx.RLock()
	ret, ok := s.mm[key]
	s.mtx.RUnlock()
	return ret, ok

}

func (s *safemap) Delete(key uint) {
	s.mtx.RLock()
	delete(s.mm, key)
	s.mtx.RUnlock()
}

type mapShard struct {
	l        uint32
	shards   []safemap
	hashFunc func(string) uint32
	hs       hash.Hash32
}

func NewMapShard(shardNums uint) Shards {
	shards := make([]safemap, shardNums)
	for i := uint(0); i < shardNums; i++ {
		shards[i] = newSafemap()
	}

	sd := &mapShard{
		l:      uint32(shardNums),
		shards: shards,
	}
	sd.UseFnvHash()
	return sd
}

func (sd *mapShard) UseFnvHash() {
	sd.hs = fnv.New32()
	sd.hashFunc = func(s string) uint32 {
		sd.hs.Reset()
		sd.hs.Write([]byte(s))
		return sd.hs.Sum32() % sd.l
	}
}

func (sd *mapShard) UseAdler32Hash() {
	sd.hs = adler32.New()
	sd.hashFunc = func(s string) uint32 {
		sd.hs.Reset()
		sd.hs.Write([]byte(s))
		return sd.hs.Sum32() % sd.l
	}
}

func (sd *mapShard) UseCrc32Hash() {
	sd.hs = crc32.New(crc32.IEEETable)
	sd.hashFunc = func(s string) uint32 {
		sd.hs.Reset()
		sd.hs.Write([]byte(s))
		return sd.hs.Sum32() % sd.l
	}
}

func (sd *mapShard) SetHashFunc(hf func(string) uint32) {
	sd.hashFunc = hf
}

func (s *mapShard) Set(key uint, val any) {
	s.shards[s.hashFunc(fmt.Sprintf("%d", key))].Set(key, val)
}

func (s *mapShard) Get(userId uint) (any, bool) {
	return s.shards[s.hashFunc(fmt.Sprintf("%d", userId))].Get(userId)
}

func (s *mapShard) Delete(userId uint) {
	s.shards[s.hashFunc(fmt.Sprintf("%d", userId))].Delete(userId)
}
